#include "System.h"
#include "resource.h"
#include <fstream>
using namespace std;

static HWND g_hDlg;

static UINT g_uReflect = 1;
static UINT g_uNoise = 1;
static UINT g_uNoiseUnder = 1;
static UINT g_uRefract = 1;
static UINT g_uShadow = 1;
static UINT g_uNormal = 1;
static UINT g_uLightRotate = 1;
static UINT g_uTeapotRotate = 1;

static int g_iRgbPos[24] = {
												90, 90, 90, 60,				//DiffuseMaterial
												100, 100, 100, 100,		//SpecularMaterial
												50, 50, 50, 60,				//AmbientMaterial

												100, 100, 100, 100,		//DiffuseColor
												70, 70, 70, 32,				//SpecularColor
												30, 30, 30, 100				//AmbientColor
											};
static int g_iXyzPos[3] = {222, 95, 255};

System::System():
m_pGraphics(NULL),
m_pInput(NULL),
m_Fps(NULL),
m_Cpu(NULL),
m_Timer(NULL),
m_bFULL_SCREEN(false),
m_bVSYNC_ENABLED(true),
SCREEN_DEPTH(1000.0f),
SCREEN_NEAR(0.1f)
{

}

System::System( const System& )
{

}

System::~System()
{

}

bool System::Initialize()
{
	int iScreenWidth = 0;
	int iScreenHeight = 0;
	bool bResult = false;

	int bVSYNC = 0;
	int bFULLSCREEN = 0;
	fstream file;
	file.open("data//setting.txt", ios::in);
	file >> bVSYNC >> bFULLSCREEN;
	file.close();

	m_bVSYNC_ENABLED =  bVSYNC? true : false;
	m_bFULL_SCREEN = bFULLSCREEN? true : false;

	InitializeWindows(iScreenWidth, iScreenHeight);

	m_pInput = new Input;
	if (!m_pInput)
	{
		return false;
	}
	// Initialize the input object.
	bResult = m_pInput->Initialize(m_hinstance, m_hwnd, iScreenWidth, iScreenHeight);
	if(!bResult)
	{
		MessageBox(m_hwnd, L"Could not initialize the input object.", L"Error", MB_OK);
		return false;
	}

	m_pGraphics = new Graphics;
	if (!m_pGraphics)
	{
		return false;
	}
	bResult = m_pGraphics->Initialize(iScreenWidth, iScreenHeight, m_hwnd, g_hDlg, m_pInput,
		m_bVSYNC_ENABLED, m_bFULL_SCREEN, SCREEN_DEPTH, SCREEN_NEAR);
	if (!bResult)
	{
		return false;
	}

	// Create the fps object.
	m_Fps = new FpsClass;
	if(!m_Fps)
	{
		return false;
	}

	// Initialize the fps object.
	m_Fps->Initialize();

	// Create the cpu object.
	m_Cpu = new CpuClass;
	if(!m_Cpu)
	{
		return false;
	}

	// Initialize the cpu object.
	m_Cpu->Initialize();

	// Create the timer object.
	m_Timer = new TimerClass;
	if(!m_Timer)
	{
		return false;
	}

	// Initialize the timer object.
	bResult = m_Timer->Initialize();
	if(!bResult)
	{
		MessageBox(m_hwnd, L"Could not initialize the Timer object.", L"Error", MB_OK);
		return false;
	}

	return true;
}

void System::Shutdown()
{
	// Release the timer object.
	if(m_Timer)
	{
		delete m_Timer;
		m_Timer = 0;
	}

	// Release the cpu object.
	if(m_Cpu)
	{
		m_Cpu->Shutdown();
		delete m_Cpu;
		m_Cpu = 0;
	}

	// Release the fps object.
	if(m_Fps)
	{
		delete m_Fps;
		m_Fps = 0;
	}

	if (m_pGraphics)
	{
		m_pGraphics->Shutdown();
		delete m_pGraphics;
		m_pGraphics = NULL;
	}

	if (m_pInput)
	{
		m_pInput->Shutdown();
		delete m_pInput;
		m_pInput = NULL;
	}

	ShutdownWindows();
}

void System::Run()
{
	MSG msg;
	bool bDone = false;
	bool bResult = false;

	ZeroMemory(&msg, sizeof(MSG));

	while (!bDone)
	{
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			if (g_hDlg == 0 || !IsDialogMessage (g_hDlg, &msg))
			{
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
		}

		if(msg.message == WM_QUIT)
		{
			bDone = true;
		}
		else
		{
			// Otherwise do the frame processing.
			static float fPreTime = static_cast<float>(timeGetTime());
			float fCurrentTime = static_cast<float>(timeGetTime());
			float fDeltaTime = (fCurrentTime - fPreTime) * 0.001f;
			bResult = Frame(fDeltaTime);
			fPreTime = fCurrentTime;
			if(!bResult)
			{
				bDone = true;
			}
		}
		// Check if the user pressed escape and wants to quit.
		if(m_pInput->IsEscapePressed() == true)
		{
			bDone = true;
		}
	}
}

LRESULT CALLBACK System::MessageHandler( HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam )
{
	return DefWindowProc(hwnd, umsg, wparam, lparam);
}

bool System::Frame(float fTime)
{
	bool bResult = false;
	int iMouseX, iMouseY;

	SetParam();
	// Update the system stats.
	m_Timer->Frame();
	m_Fps->Frame();
	m_Cpu->Frame();

	// Do the input frame processing.
	bResult = m_pInput->Frame();
	if(!bResult)
	{
		return false;
	}
	m_pInput->GetMouseLocation(iMouseX, iMouseY);

	bResult = m_pGraphics->Frame(m_Fps->GetFps(), m_Cpu->GetCpuPercentage(), m_Timer->GetTime(), fTime);
	if (!bResult)
	{
		return false;
	}

	return true;
}

void System::InitializeWindows( int& screenWidth, int& screenHeight )
{
	WNDCLASSEX wc;
	DEVMODE dmScreenSettings;
	int iPosX = 0;
	int iPosY = 0;

	g_pApplicationHandle = this;
	
	m_hinstance = GetModuleHandle(NULL);
	m_applicationName = L"123022009007";

	wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
	wc.lpfnWndProc   = WndProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = m_hinstance;
	wc.hIcon		 = LoadIcon(m_hinstance, MAKEINTRESOURCE(IDI_ICON));
	wc.hIconSm       = wc.hIcon;
	wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = m_applicationName;
	wc.cbSize        = sizeof(WNDCLASSEX);
	
	RegisterClassEx(&wc);

	screenWidth  = GetSystemMetrics(SM_CXSCREEN);
	screenHeight = GetSystemMetrics(SM_CYSCREEN);

	if(m_bFULL_SCREEN)
	{
		// If full screen set the screen to maximum size of the users desktop and 32bit.
		memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
		dmScreenSettings.dmSize       = sizeof(dmScreenSettings);
		dmScreenSettings.dmPelsWidth  = (unsigned long)screenWidth;
		dmScreenSettings.dmPelsHeight = (unsigned long)screenHeight;
		dmScreenSettings.dmBitsPerPel = 32;			
		dmScreenSettings.dmFields     = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;

		// Change the display settings to full screen.
		ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN);

		// Set the position of the window to the top left corner.
		iPosX = iPosY = 0;
	}
	else
	{
		// If windowed then set it to 800x600 resolution.
		screenWidth  = 800;
		screenHeight = 600;

		// Place the window in the middle of the screen.
		iPosX = (GetSystemMetrics(SM_CXSCREEN) - screenWidth)  / 2;
		iPosY = (GetSystemMetrics(SM_CYSCREEN) - screenHeight) / 2;
	}

	// Create the window with the screen settings and get the handle to it.
	m_hwnd = CreateWindowEx(WS_EX_APPWINDOW, m_applicationName, m_applicationName, 
		WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW,//WS_POPUP
		iPosX, iPosY, screenWidth, screenHeight, NULL, NULL, m_hinstance, NULL);

	g_hDlg = CreateDialog(m_hinstance, MAKEINTRESOURCE(IDD_PARAM), m_hwnd, ColorScrDlg);
	// Bring the window up on the screen and set it as main focus.
	ShowWindow(m_hwnd, SW_SHOW);
	SetForegroundWindow(m_hwnd);
	SetFocus(m_hwnd);

	//// Hide the mouse cursor.
	//ShowCursor(false);
}

void System::ShutdownWindows()
{
	// Show the mouse cursor.
	ShowCursor(true);

	// Fix the display settings if leaving full screen mode.
	if(m_bFULL_SCREEN)
	{
		ChangeDisplaySettings(NULL, 0);
	}

	// Remove the window.
	DestroyWindow(m_hwnd);
	m_hwnd = NULL;

	// Remove the application instance.
	UnregisterClass(m_applicationName, m_hinstance);
	m_hinstance = NULL;

	// Release the pointer to this class.
	g_pApplicationHandle = NULL;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT umessage, WPARAM wparam, LPARAM lparam)
{
	switch(umessage)
	{
		// Check if the window is being destroyed.
	case WM_DESTROY:
		{
			DestroyWindow(g_hDlg);
			g_hDlg = NULL;
			PostQuitMessage(0);
			return 0;
		}

		// Check if the window is being closed.
	case WM_CLOSE:
		{
			DestroyWindow(g_hDlg);
			g_hDlg = NULL;
			PostQuitMessage(0);		
			return 0;
		}

		// All other messages pass to the message handler in the system class.
	default:
		{
			return g_pApplicationHandle->MessageHandler(hwnd, umessage, wparam, lparam);
		}
	}
}

BOOL CALLBACK ColorScrDlg(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	HWND hCtrl = NULL;
	int iCtrlID = 0;
	int iIndex = 0;

	switch(message)
	{
	case WM_INITDIALOG:
		{
			CheckDlgButton(hDlg, IDC_CHECK_REFLECT, 1);
			CheckDlgButton(hDlg, IDC_CHECK_NOISE, 1);
			CheckDlgButton(hDlg, IDC_CHECK_NOISE_UNDER, 1);
			CheckDlgButton(hDlg, IDC_CHECK_NORMAL, 1);
			CheckDlgButton(hDlg, IDC_CHECK_REFRACT, 1);
			CheckDlgButton(hDlg, IDC_CHECK_SHADOW, 1);
			CheckDlgButton(hDlg, IDC_CHECK_LIGHT_ROTATE, 1);
			CheckDlgButton(hDlg, IDC_CHECK_TEAPOT_ROTATE, 1);

			for (int i = 0; i < 24; ++i)
			{
				hCtrl = GetDlgItem (hDlg, IDC_SCROLLBAR_DIFFMR_R + i) ;
				SetScrollRange (hCtrl, SB_CTL, 0, 100, FALSE) ;
				SetScrollPos  (hCtrl, SB_CTL, g_iRgbPos[i], FALSE) ;
				float fPos = static_cast<float>(g_iRgbPos[i]) * 0.01f;
				char cPos[8] = {0};
				WCHAR wcPos[8] = {0};
				sprintf_s(cPos, 8, "%.2f", fPos);
				MultiByteToWideChar(CP_ACP, 0, cPos, -1, wcPos, 8);
				SetDlgItemText(hDlg, IDC_SCROLLBAR_DIFFMR_R + i +24, wcPos);
			}

			for (int i = 0; i < 3; ++i)
			{
				hCtrl = GetDlgItem (hDlg, IDC_SCROLLBAR_LTDR_X + i) ;
				SetScrollRange (hCtrl, SB_CTL, 0, 400, FALSE) ;
				SetScrollPos  (hCtrl, SB_CTL, g_iXyzPos[i], FALSE) ;
				float fPos = static_cast<float>(g_iXyzPos[i] - 200) * 0.01f;
				char cPos[8] = {0};
				WCHAR wcPos[8] = {0};
				sprintf_s(cPos, 8, "%.2f", fPos);
				MultiByteToWideChar(CP_ACP, 0, cPos, -1, wcPos, 8);
				SetDlgItemText(hDlg, IDC_SCROLLBAR_LTDR_X + i +3, wcPos);
			}
		}
		return true;
	case   WM_HSCROLL :
		{
			hCtrl = (HWND) lParam ;
			iCtrlID = GetWindowLong (hCtrl, GWL_ID) ;
			if (iCtrlID < IDC_SCROLLBAR_LTDR_X)
			{
				iIndex = iCtrlID - IDC_SCROLLBAR_DIFFMR_R;
				switch (LOWORD (wParam))
				{
				case   SB_PAGEDOWN :
					{
						g_iRgbPos[iIndex] += 20;
					}
					break;
				case   SB_PAGEUP  :
					{
						g_iRgbPos[iIndex] -= 20;
					}
					break;
				case   SB_LINELEFT  :
					{
						g_iRgbPos[iIndex] = max (0, g_iRgbPos[iIndex] - 1) ;
					}
					break;
				case   SB_LINERIGHT  :
					{
						g_iRgbPos[iIndex] = min (100, g_iRgbPos[iIndex] + 1) ;
					}
					break;
				case   SB_THUMBTRACK   :
					{
						g_iRgbPos[iIndex] = HIWORD (wParam);
					}
					break;
				default:
					return false;
				}
				SetScrollPos(hCtrl, SB_CTL, g_iRgbPos[iIndex], TRUE);
				float fPos = static_cast<float>(g_iRgbPos[iIndex]) * 0.01f;
				char cPos[8] = {0};
				WCHAR wcPos[8] = {0};
				sprintf_s(cPos, 8, "%.2f", fPos);
				MultiByteToWideChar(CP_ACP, 0, cPos, -1, wcPos, 8);
				SetDlgItemText(hDlg, iCtrlID + 24, wcPos);
			}
			else
			{
				iIndex = iCtrlID - IDC_SCROLLBAR_LTDR_X;
				switch (LOWORD (wParam))
				{
				case   SB_PAGEDOWN :
					{
						g_iXyzPos[iIndex] += 80;
					}
					break;
				case   SB_PAGEUP  :
					{
						g_iXyzPos[iIndex] -= 80;
					}
					break;
				case   SB_LINELEFT  :
					{
						g_iXyzPos[iIndex] = max (0, g_iXyzPos[iIndex] - 1) ;
					}
					break;
				case   SB_LINERIGHT  :
					{
						g_iXyzPos[iIndex] = min (400, g_iXyzPos[iIndex] + 1) ;
					}
					break;
				case   SB_THUMBTRACK   :
					{
						g_iXyzPos[iIndex] = HIWORD (wParam);
					}
					break;
				default:
					return false;
				}
				SetScrollPos(hCtrl, SB_CTL, g_iXyzPos[iIndex], TRUE);
				float fPos = static_cast<float>(g_iXyzPos[iIndex] - 200) * 0.01f;
				char cPos[8] = {0};
				WCHAR wcPos[8] = {0};
				sprintf_s(cPos, 8, "%.2f", fPos);
				MultiByteToWideChar(CP_ACP, 0, cPos, -1, wcPos, 8);
				SetDlgItemText(hDlg, iCtrlID + 3, wcPos);
			}
		}
		break;
	case WM_CLOSE:
		ShowWindow(hDlg, SW_HIDE);
		break;
	case WM_COMMAND:
		{
			g_uReflect = IsDlgButtonChecked (hDlg, IDC_CHECK_REFLECT);
			g_uNoise = IsDlgButtonChecked (hDlg, IDC_CHECK_NOISE);
			g_uNoiseUnder = IsDlgButtonChecked (hDlg, IDC_CHECK_NOISE_UNDER);
			g_uNormal = IsDlgButtonChecked (hDlg, IDC_CHECK_NORMAL);
			g_uRefract = IsDlgButtonChecked (hDlg, IDC_CHECK_REFRACT);
			g_uShadow = IsDlgButtonChecked (hDlg, IDC_CHECK_SHADOW);
			g_uLightRotate = IsDlgButtonChecked (hDlg, IDC_CHECK_LIGHT_ROTATE);
			g_uTeapotRotate = IsDlgButtonChecked (hDlg, IDC_CHECK_TEAPOT_ROTATE);
		}

		break;
	}
	return FALSE;
}

void System::SetParam()
{  
	float fRGB[24] = {0.0f};
	for (int i = 0; i < 24; ++i)
	{
		fRGB[i] = static_cast<float>(g_iRgbPos[i]) * 0.01f;
	}

	float fXYZ[3] = {0.0f};
	for (int i = 0; i < 3; ++i)
	{
		fXYZ[i] = static_cast<float>(g_iXyzPos[i] - 200) * 0.01f;
	}
	m_pGraphics->SetParam(g_uReflect, g_uNoise, g_uNoiseUnder, g_uNormal, 
		g_uRefract, g_uShadow, g_uLightRotate, g_uTeapotRotate, fRGB, fXYZ);
}